﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using Framework.CustomConfigurationSectionsDefinition;
using Microsoft.Practices.Unity;
using System.Configuration;

namespace Framework
{
    public static class ProvidersManager
    {
        private static readonly IUnityContainer _container ;
        
        private static CustomDataProvidersConfigSection providersConfigSection;

        private static UnityBindingConfigSection unityBindingConfigSection;

        static ProvidersManager()
        {
            LoadConfig("customDataProviders", "unityBindings");
            _container = new UnityContainer();
            RegisterUnityType();
        }

        public static IEnumerable<T> ResolveDataProviders<T>()
        {
            var instanceList=new LinkedList<T>();
            foreach (UnityBindingElement element in providersConfigSection.Providers)
            {
                var instanceAssemblyName = element.AssemblyInstance;
                var instName = element.Class;
                var interfName = element.Interface;

                if(interfName.Equals(typeof(T).FullName))
                {
                    var assm = GetAssembly(instanceAssemblyName);
                    var instType = assm.GetType(instName);
                    IUnityContainer unityContainer = new UnityContainer();
                    unityContainer.RegisterType(typeof(T),instType,null,GetLifeTimeManager(element.Lifetime));
                    instanceList.AddLast(unityContainer.Resolve<T>());
                }
            }
            return instanceList;
        }

        #region Wrapper members

        public static IEnumerable<T> ResolveAll<T>()
        {
            return _container.ResolveAll<T>();
        }

        public static T Resolve<T>()
        {
            return _container.Resolve<T>();
        }

        public static IUnityContainer RegisterInstance<T>(T instance)
        {
            return _container.RegisterInstance(instance);
        }

        public static IUnityContainer RegisterType<T,TV>() 
            where TV : T
        {
            return _container.RegisterType<T,TV>();
        }

        #endregion
        
        private static void LoadConfig(string providerSectionName,string unitySectionName)
        {
            var filemap = new ExeConfigurationFileMap
                              {
                                  ExeConfigFilename =
                                      Environment.CurrentDirectory + ConfigurationManager.AppSettings["configName"]
                              };

            var mappedConfig = ConfigurationManager.OpenMappedExeConfiguration(filemap,ConfigurationUserLevel.None);
            
            providersConfigSection = mappedConfig.GetSection(providerSectionName) as CustomDataProvidersConfigSection;
            unityBindingConfigSection = mappedConfig.GetSection(unitySectionName) as UnityBindingConfigSection;

            if (unityBindingConfigSection == null)
            {
                throw new ConfigurationErrorsException(
                    string.Format("Invalid configuration section name {0}. It section doesn't find", unityBindingConfigSection));
            }

            if(providersConfigSection==null)
            {
                throw new ConfigurationErrorsException(
                    string.Format("Invalid configuration section name {0}. It section doesn't find",providerSectionName));
            }
        }

        private static void RegisterUnityType()
        {
            foreach (UnityBindingElement unityMappingElement in unityBindingConfigSection.UnityBinding)
            {
                var interfaceAssemblyName = unityMappingElement.AssemblyInterface;
                var instanceAssemblyName = unityMappingElement.AssemblyInstance;
                var instName = unityMappingElement.Class;
                var interfName = unityMappingElement.Interface;
                
                var interfAssembly = GetAssembly(interfaceAssemblyName);
                var instAssembly = GetAssembly(instanceAssemblyName);

                Type intType = interfAssembly.GetType(interfName);
                Type instType = instAssembly.GetType(instName);

                _container.RegisterType(intType, instType,null,GetLifeTimeManager(unityMappingElement.Lifetime));
            }
        }

        private static Assembly GetAssembly(string name)
        {
            AppDomain currentDomain = AppDomain.CurrentDomain;
            Assembly[] assemblies = currentDomain.GetAssemblies();
            Assembly assembly = assemblies.SingleOrDefault(assm => assm.FullName.Equals(name)) ?? Assembly.Load(name);
            return assembly;
        }

        private static LifetimeManager GetLifeTimeManager(string key)
        {
            switch (key)
            {
                case "perThread": return new PerThreadLifetimeManager();
                case "singleton": return new PerResolveLifetimeManager();
                default:
                    return null;
            }
        }
    }
}
 